home *** CD-ROM | disk | FTP | other *** search
/ Megadoom II / MEGADOOM II - iso.7z / MEGADOOM II.ISO / doom / editors / wadfile / warm11 / warm.c < prev    next >
C/C++ Source or Header  |  1995-01-17  |  47KB  |  1,142 lines

  1. /******************************************************************************
  2.     PROGRAM:    WARM.C
  3.     WRITTEN BY:    Robert Fenske, Jr. (rfenske@swri.edu)
  4.                 Southwest Research Institute
  5.                 Electromagnetics Division
  6.                 6220 Culebra
  7.                 San Antonio, Texas 78238-5166
  8.     CREATED:    May  1994
  9.     DESCRIPTION:    This program is the WAD Auxiliary Resource Manipulator.
  10.             It extracts data from a DOOM-related PWAD, IWAD, or
  11.             VERDA patch file and builds the BSP-related structures
  12.             segs, subsectors, and nodes; the blockmap structure;
  13.             and the reject structure.  It can build these
  14.             structures for all the levels in the input file, or for
  15.             a specific level found in the input file.  The main
  16.             command line arguments are as follows:
  17.  
  18.             [-e#m#] [-z] [-x=<list>] [-n] [-b] [-r]
  19.             <input file> [output file]
  20.  
  21.             where -e#m# specifies a particular level within the
  22.             input file (use -map## for DOOM II WAD files), -z
  23.             specifies to generate a zero-filled reject block (much
  24.             faster than building a full REJECT), -x=<list>
  25.             specifies a sector exception list for the reject block,
  26.             input file is the input filename, and output file is
  27.             the optional output filename.  If no output file is
  28.             specified, the input file is rewritten with the new
  29.             data.
  30.  
  31.             It also contains various functions to manipulate
  32.             various resources in a DOOM-related IWAD, PWAD, or
  33.             VERDA patch file.
  34.  
  35.             This program has been compiled and run under SunOS
  36.             using cc(1), under MS-DOS using DJGPP (GCC) and
  37.             under OS/2 using C/Set++.
  38.  
  39.             DOOM is a trademark of id Software, Inc.
  40. ******************************************************************************/
  41. #include <stdio.h>
  42. #include <stdlib.h>
  43. #include <string.h>
  44. #if !defined(__OS2__)
  45. #include <values.h>
  46. #endif
  47. #include <math.h>
  48. #include <time.h>
  49. #include "dmglobal.i"
  50.  
  51. #define SOFTVER        1.1            /* software version */
  52. #define SOFTDATE    "January 1995"        /* software version date */
  53.  
  54. #define RESOURCES_NEEDED ((long)((1<<THINGS)|(1<<LINES)|(1<<SIDES)|(1<<VERTS)|\
  55.                      (1<<SEGS)|(1<<SSECTS)|(1<<NODES)|(1<<SECTS)))
  56.  
  57. #define help(t)        printf("%*s %s\n",(int)strlen(prog),"",(t))
  58.  
  59. #define is_furniture(t)    \
  60.         ((0x001<=(t).item&&(t).item<=0x004) ||/* player starts     */\
  61.          (t).item==0x00E ||            /* teleport            */\
  62.          (0x019<=(t).item&&(t).item<=0x039) ||/* various columns,  */\
  63.          (0x03B<=(t).item&&(t).item<=0x03F) ||/* skulls, prisoners */\
  64.          (t).item==0x046 ||            /* burning barrel      */\
  65.          (0x048<=(t).item&&(t).item<=0x051) ||/* various hanging   */\
  66.          (t).item==0x7EC || (t).item==0x7F3)/* light post,barrel   */
  67. #define is_deathmatch(t) ((t).item==0x00B)    /* deathmatch */
  68. #define is_creature(t)    ((t).item==0x0007 ||    /* spider demon    */\
  69.              (t).item==0x0009 ||    /* former sargent  */\
  70.              (t).item==0x0010 ||    /* cyborg baron    */\
  71.              (t).item==0x003A ||    /* spectre         */\
  72.              (t).item==0x0040 ||    /* archvile        */\
  73.              (t).item==0x0041 ||    /* former commando */\
  74.              (t).item==0x0042 ||    /* revenant        */\
  75.              (t).item==0x0043 ||    /* mancubus        */\
  76.              (t).item==0x0044 ||    /* arachnotron     */\
  77.              (t).item==0x0045 ||    /* hell knight     */\
  78.              (t).item==0x0047 ||    /* pain elemental  */\
  79.              (t).item==0x0054 ||    /* Wolfenstein SS  */\
  80.              (t).item==0x0057 ||    /* spawn spot      */\
  81.              (t).item==0x0058 ||    /* boss brain      */\
  82.              (t).item==0x0059 ||    /* boss shooter    */\
  83.              (t).item==0x0BB9 ||    /* imp             */\
  84.              (t).item==0x0BBA ||    /* pink demon      */\
  85.              (t).item==0x0BBB ||    /* baron of hell   */\
  86.              (t).item==0x0BBC ||    /* former human    */\
  87.              (t).item==0x0BBD ||    /* cacodemon       */\
  88.              (t).item==0x0BBE)    /* lost soul       */
  89. #define is_boss(t)    ((t).item==0x0007 ||    /* spider demon    */\
  90.              (t).item==0x0010 ||    /* cyborg baron    */\
  91.              (t).item==0x0BBB)    /* baron of hell   */
  92. #define is_weapon(t)    ((t).item==0x0052 ||    /* super shotgun   */\
  93.              (t).item==0x07D1 ||    /* shotgun         */\
  94.              (t).item==0x07D2 ||    /* chain gun       */\
  95.              (t).item==0x07D3 ||    /* rocket launcher */\
  96.              (t).item==0x07D4 ||    /* plasma rifle    */\
  97.              (t).item==0x07D5 ||    /* chainsaw        */\
  98.              (t).item==0x07D6)    /* BFG9000         */
  99.  
  100. #define Things        ((DOOM_THING *)iwad->data[e+THINGS])
  101. #define Lines        ((DOOM_LINE *)iwad->data[e+LINES])
  102. #define Sides        ((DOOM_SIDE *)iwad->data[e+SIDES])
  103. #define Verts        ((DOOM_VERT *)iwad->data[e+VERTS])
  104. #define Segs        ((DOOM_SEGS *)iwad->data[e+SEGS])
  105. #define Ssecs        ((DOOM_SSECTOR *)iwad->data[e+SSECTS])
  106. #define Nodes        ((DOOM_NODE *)iwad->data[e+NODES])
  107. #define Sects        ((DOOM_SECTOR *)iwad->data[e+SECTS])
  108. #define Rejects        ((DOOM_REJECT *)iwad->data[e+REJECTS])
  109. #define Blockmaps    ((DOOM_BLOCKMAP *)iwad->data[e+BLKMAPS])
  110.  
  111. #define NThings        iwad->count[e+THINGS]
  112. #define NLines        iwad->count[e+LINES]
  113. #define NSides        iwad->count[e+SIDES]
  114. #define NVerts        iwad->count[e+VERTS]
  115. #define NSegs        iwad->count[e+SEGS]
  116. #define NSsecs        iwad->count[e+SSECTS]
  117. #define NNodes        iwad->count[e+NODES]
  118. #define NSects        iwad->count[e+SECTS]
  119. #define NRejects    iwad->count[e+REJECTS]
  120. #define NBlockmaps    iwad->count[e+BLKMAPS]
  121.  
  122. #if defined(sun)
  123. extern void srand48();
  124. extern double drand48();
  125. #elif (__OS2__)
  126. #include <limits.h>
  127. #define srand48(s)    srand(s)
  128. #define drand48()    ((double)rand()/(double)SHRT_MAX)
  129. #else
  130. #define srand48(s)    srandom(s)
  131. #define drand48()    ((double)random()/(double)MAXINT)
  132. #endif
  133.  
  134. #if defined(ANSI_C)
  135. int dmit(int ver,register DOOM_THING *things,long nthings,DOOM_SECTOR *sects,
  136.          long nsects);
  137. int _Optlink rand_xy_sort(register const void *thing1,
  138.                           register const void *thing2);
  139. int _Optlink rand_item_sort(register const void *thing1,
  140.                             register const void *thing2);
  141. int rand_thing(long seed, register DOOM_THING *things, long nthings);
  142. int flip(DOOM_THING *things, long nthings, DOOM_VERT *verts, long nverts,
  143.          DOOM_LINE *lines, long nlines, DOOM_NODE *nodes, long nnodes,
  144.          DOOM_SEGS *segs, long nsegs);
  145. int shift(int dx,int dy,int dz,DOOM_THING *things, long nthings,
  146.           DOOM_VERT *verts, long nverts, DOOM_NODE *nodes, long nnodes,
  147.           DOOM_SECTOR *sects, long nsects);
  148. int emstat(register WAD_INFO *iwad, register int e);
  149. int directory(register WAD_INFO *iwad);
  150. int extract(register WAD_INFO *iwad, char *list);
  151. int substitute(register WAD_INFO *iwad, char *list);
  152. int merge(register WAD_INFO *iwad, int e, char *list);
  153. #endif
  154.  
  155.  
  156. /******************************************************************************
  157.     ROUTINE:    main(ac,av)
  158.     WRITTEN BY:    Robert Fenske, Jr.
  159.     CREATED:    Apr. 1994
  160.     DESCRIPTION:    This routine processes the command line arguments
  161.             and executes the appropriate manipulation function.
  162. ******************************************************************************/
  163. #if defined(ANSI_C)
  164. int main(int ac, char *av[])
  165. #else
  166. int main(ac,av)
  167. int ac;
  168. char *av[];
  169. #endif
  170. {
  171.   static char func[256] = "";            /* manipulating function(s) */
  172.   static char rxcpt_list[256] = "";        /* REJECT exception list */
  173.   static char extract_list[256] = "",        /* resource extraction list */
  174.               subst_list[256] = "",        /* rsrce substitution list */
  175.               merge_list[256] = "";        /* merge file list */
  176.   char *ifile = NULL, *ofile = NULL;        /* input/output filenames */
  177.   char *prog;                    /* program name */
  178.   WAD_INFO *iwad, *owad;            /* input/output info blocks */
  179.   boolean show_help = FALSE;            /* whether to display help */
  180.   boolean good = FALSE;                /* whether read/write worked */
  181.   int epdo = 0, mpdo = 0;            /* specific ep/map to do */
  182.   int outtype = 0;                /* specified output type */
  183.   boolean zeroflag = 0;                /* don't make zero REJECT */
  184.   int dmver = 1;                /* deathmatch version # */
  185.   long seed;                    /* randomize seed */
  186.   int dx = 0, dy = 0, dz = 0;            /* shift by dx, dy, dz */
  187.   int e = -1;                    /* current directory entry */
  188.   time_t begintime, totaltime, hours, mins, secs;
  189.   register int a;
  190.  
  191.   begintime = time(NULL);
  192.   setbuf(stdout,(char *)NULL);            /* make stdout unbuffered */
  193.   printf("\n\t\t\tWAD Auxiliary Resource Manipulator\n");
  194.   printf("   Version %3.1f of %s by Robert Fenske, Jr (rfenske@swri.edu)\n\
  195.     Ported to OS/2 2.1 by Mark K. Mathews (mark.mathews@channel1.com)\n\n",
  196.          SOFTVER,SOFTDATE);
  197.   for (a = 1; a < ac; a++) {            /* scan all arguments */
  198.     if (av[a][0] == '-') {            /* optional argument */
  199.       if (2 == sscanf(av[a],"-%*[Ee]%d%*[Mm]%d",&epdo,&mpdo))
  200.         ;
  201.       else if (1 == sscanf(av[a],"-map%d",&mpdo))
  202.         epdo = 4;
  203.       else if (1 == sscanf(av[a],"-MAP%d",&mpdo))
  204.         epdo = 4;
  205.       else if (0 == strcmp(av[a],"-z"))
  206.         zeroflag = 1;
  207.       else if (1 == sscanf(av[a],"-x=%s",rxcpt_list))
  208.         ;
  209.       else if (0 == strcmp(av[a],"-n"))
  210.         strcat(func,"nodes");
  211.       else if (0 == strcmp(av[a],"-b"))
  212.         strcat(func,"blockmap");
  213.       else if (0 == strcmp(av[a],"-r"))
  214.         strcat(func,"reject");
  215.       else if (1 == sscanf(av[a],"-dmit=%d",&dmver))
  216.         strcat(func,"dmit");
  217.       else if (1 == sscanf(av[a],"-rand=%ld",&seed))
  218.         strcat(func,"rand");
  219.       else if (0 == strcmp(av[a],"-flip"))
  220.         strcat(func,"flip");
  221.       else if (2 <= sscanf(av[a],"-shift=%d,%d,%d",&dx,&dy,&dz))
  222.         strcat(func,"shift");
  223.       else if (0 == strcmp(av[a],"-emstat"))
  224.         strcat(func,"emstat");
  225.       else if (0 == strcmp(av[a],"-dir"))
  226.         strcat(func,"dir");
  227.       else if (1 == sscanf(av[a],"-e=%s",extract_list))
  228.         ;
  229.       else if (1 == sscanf(av[a],"-s=%s",subst_list))
  230.         ;
  231.       else if (1 == sscanf(av[a],"-m=%s",merge_list))
  232.         ;
  233.       else if (1 == sscanf(av[a],"-o=%d",&outtype))/* this isn't in help */
  234.         ;
  235.       else
  236.         show_help = TRUE;
  237.     }else if (ifile == NULL)
  238.       ifile = ofile = av[a];            /* get input filename */
  239.     else
  240.       ofile = av[a];                /* get output filename */
  241.   }
  242.   prog = strrchr(av[0],'\\');
  243.   if (prog == NULL) prog = strrchr(av[0],'/');
  244.   if (prog != NULL) prog++;
  245.   else              prog = av[0];
  246.   if (strrchr(prog,'.') != NULL) *strrchr(prog,'.') = '\0';
  247.   if (ifile == NULL || show_help) {        /* show now to do corectly */
  248.     printf("%s [-e#m#] [-z] [-x=<slist>] [-n] [-b] [-r]",prog);
  249.     printf(" <input file> [output file]\n");
  250. help("[-dmit=<#>] [-rand=<#>] [-flip] [-shift=<dx,dy[,dz]>] [-emstat]");
  251. help("[-dir] [-e=<rlist>] [-s=<flist>] [-m=<flist>]\n");
  252. help("-e#m#         specify level to process; otherwise does all levels in");
  253. help("              input file (use -map## for DOOM II WAD files)");
  254. help("-z            build zero-filled REJECT--much faster than full REJECT");
  255. help("-x=<slist>    specify exceptions for REJECT; <slist> is <s>,... where");
  256. help("              <s> is <sector #>[-<sector #>]; this forces a 1 for");
  257. help("              each sector pair or for a sector against all others");
  258. help("-n            build nodes");
  259. help("-b            build blockmap");
  260. help("-r            build reject");
  261. help("<input file>  PWAD, IWAD, or VERDA patch file");
  262. help("<output file> output file; if none specified, input file is rewritten");
  263. help("               ");
  264. help("defaults to -n -b -r if none of the following options is specified;");
  265. help("otherwise, perform specified function");
  266. help("               ");
  267. help("press Enter to see other options");    /* could be more sophisti- */
  268. (void)getchar();                /* cated, but won't bother */
  269. help("               ");
  270. help("-dmit=<#>      \"deathmatch-ize\" level for version 1 or 2");
  271. help("-rand=<#>      swap creatures, bonuses, deathmatch starts positions");
  272. help("               randomly using specified seed");
  273. help("-flip          flip level about Y-axis (make mirror image)");
  274. help("-shift=<dx,dy,dz> shift level by dx, dy, dz");
  275. help("-emstat        display level statistics");
  276. help("-dir           display resource directory");
  277. help("-e=<rlist>     extract comma-separated named resources from input");
  278. help("               file to output file; resources E#M# or MAP## extract");
  279. help("               all data for that level; if starts with !, extract");
  280. help("               all resources not named");
  281. help("-s=<flist>     substitute resources in input file with resources in");
  282. help("               comma-separated PWAD, IWAD, or VERDA patch files");
  283. help("-m=<flist>     merge comma-separated PWAD, IWAD, or VERDA patch");
  284. help("               files with input file: matching E#M# level data is");
  285. help("               merged, unique resources are added\n");
  286. help("dir, extract, substitute, and merge are mutually exclusive with all");
  287. help("other functions and ignore any -e#m# or -map## option");
  288.     printf("\n\t\t\tWAD Auxiliary Resource Manipulator\n");
  289.     printf("   Version %3.1f of %s by Robert Fenske, Jr (rfenske@swri.edu)\n\
  290.     Ported to OS/2 2.1 by Mark K. Mathews (mark.mathews@channel1.com)\n",
  291.            SOFTVER,SOFTDATE);
  292.     return 1;
  293.   }
  294.   if (strstr(func,"dir") != NULL && strcmp(func,"dir") != 0) {
  295.     fprintf(stderr,
  296.             "directory function is mutually exclusive with other functions\n");
  297.     return 1;
  298.   }
  299.   if (strlen(extract_list) > 0 && strlen(func) > 0) {
  300.     fprintf(stderr,
  301.             "extract function is mutually exclusive with other functions\n");
  302.     return 1;
  303.   }
  304.   if (strlen(subst_list) > 0 && strlen(func) > 0) {
  305.     fprintf(stderr,
  306.             "sustitute function is mutually exclusive with other functions\n");
  307.     return 1;
  308.   }
  309.   if (strlen(merge_list) > 0 && strlen(func) > 0) {
  310.     fprintf(stderr,
  311.             "merge function is mutually exclusive with other functions\n");
  312.     return 1;
  313.   }
  314.   if ((strlen(extract_list) > 0 || strlen(merge_list) > 0)
  315.       && strcmp(ifile,ofile) == 0) {
  316.     fprintf(stderr,"output file must be different from input file\n");
  317.     return 1;
  318.   }
  319.   if (strlen(extract_list) == 0 &&        /* if no specified functions */
  320.       strlen(subst_list) == 0 &&        /* do default of building    */
  321.       strlen(merge_list) == 0 &&        /* nodes, blockmap, reject   */
  322.       strlen(func) == 0)
  323.     strcpy(func,"nodes,blockmap,reject");
  324.   iwad = wad_open(ifile,TRUE,FALSE);        /* open input file */
  325.   if (iwad == NULL) {
  326.     fprintf(stderr,"unable to open %s for reading\n",ifile);
  327.     return 1;
  328.   }
  329.   if (iwad->type == 0) {            /* not a valid file */
  330.     fprintf(stderr,"%s is not a PWAD, IWAD, nor VERDA patch file\n",ifile);
  331.     return 1;
  332.   }
  333.   if ((owad = wad_open(ofile,FALSE,strcmp(ifile,ofile)==0)) == NULL) {
  334.     fprintf(stderr,"unable to open %s for writing\n",ofile);
  335.     return 1;
  336.   }
  337.   owad->type = iwad->type;            /* set output file type */
  338.   if (outtype && strcmp(ifile,ofile)) owad->type = outtype;
  339.   do {                        /* process file until done */
  340.     printf("\nReading %s file %s...",iwad->type==1?"WAD":"patch",ifile);
  341.     for (e++; e < iwad->head.count; e++)    /* find next map level */
  342.       if (strstr(func,"dir") != NULL ||
  343.           strlen(extract_list) || strlen(subst_list) || strlen(merge_list))
  344.         break;
  345.       else
  346.         if ((2==sscanf(iwad->dir[e].name,"E%dM%d",&iwad->ep,&iwad->mp) ||
  347.             1==sscanf(iwad->dir[e].name,"MAP%d",&iwad->mp) && (iwad->ep=4)==4)
  348.             &&
  349.             (epdo == 0 && mpdo == 0 ||        /* find any next level */
  350.              epdo == iwad->ep && mpdo == iwad->mp))/* find specific nxt lvl */
  351.           break;
  352.     good = wad_read(iwad,e,RESOURCES_NEEDED);
  353.     if (good) {                    /* process new level data */
  354.       printf("done\n");
  355.       if (strstr(func,"nodes") != NULL) {
  356.         DOOM_LINE *lines = Lines;
  357.         DOOM_VERT *verts = Verts;
  358.         DOOM_SEGS *segs = Segs;
  359.         DOOM_SSECTOR *ssecs = Ssecs;
  360.         DOOM_NODE *nodes = Nodes;
  361.         printf("Building BSP tree for level E%dM%d...",iwad->ep,iwad->mp);
  362.         nodes_make(&nodes,&NNodes,&ssecs,&NSsecs,&segs,&NSegs,&verts,&NVerts,
  363.                    &lines,&NLines);
  364.         resource_update(iwad,e+VERTS,verts,NVerts);/* vertices modified */
  365.         resource_update(iwad,e+SEGS,segs,NSegs);/* these three created */
  366.         resource_update(iwad,e+SSECTS,ssecs,NSsecs);
  367.         resource_update(iwad,e+NODES,nodes,NNodes);
  368.         printf("%ld nodes, %ld segs          \n",NNodes,NSegs);
  369.       }
  370.       if (strstr(func,"blockmap") != NULL) {
  371.         DOOM_BLOCKMAP *blockmaps = Blockmaps;
  372.         printf("Building BLOCKMAP for level E%dM%d...",iwad->ep,iwad->mp);
  373.         NBlockmaps = blockmap_make(&blockmaps,Lines,NLines,Verts);
  374.         resource_update(iwad,e+BLKMAPS,blockmaps,NBlockmaps);
  375.         printf("%d x %d blocks\n",blockmaps[2],blockmaps[3]);
  376.       }
  377.       if (strstr(func,"reject") != NULL) {
  378.         DOOM_REJECT *rejects = Rejects;
  379.         printf("Building REJECT for level E%dM%d...",iwad->ep,iwad->mp);
  380.         if (Blockmaps == NULL) wad_read(iwad,e,1<<BLKMAPS);
  381.         NRejects = reject_make(&rejects,zeroflag,rxcpt_list,
  382.                                Lines,NLines,Sides,Verts,Blockmaps);
  383.         resource_update(iwad,e+REJECTS,rejects,NRejects);
  384.         printf(zeroflag?"zeroed\n":"done\n");
  385.       }
  386.       if (strstr(func,"dmit") != NULL) {
  387.         printf("Deathmatch-izing (V%d.0) level E%dM%d...",
  388.                dmver,iwad->ep,iwad->mp);
  389.         dmit(dmver,Things,NThings,Sects,NSects);
  390.         resource_update(iwad,e+THINGS,Things,NThings);
  391.         resource_update(iwad,e+SECTS,Sects,NSects);
  392.         printf("done\n");
  393.       }
  394.       if (strstr(func,"rand") != NULL) {
  395.         printf("Randomizing things on level E%dM%d...",iwad->ep,iwad->mp);
  396.         rand_thing(seed,Things,NThings);
  397.         resource_update(iwad,e+THINGS,Things,NThings);
  398.         printf("done\n");
  399.       }
  400.       if (strstr(func,"flip") != NULL) {
  401.         DOOM_BLOCKMAP *blockmaps;        /* new BLOCKMAP for flip */
  402.         printf("Flipping level E%dM%d...",iwad->ep,iwad->mp);
  403.         flip(Things,NThings,Verts,NVerts,Lines,NLines,Nodes,NNodes,Segs,NSegs);
  404.         NBlockmaps = blockmap_make(&blockmaps,Lines,NLines,Verts);
  405.         resource_update(iwad,e+THINGS,Things,NThings);
  406.         resource_update(iwad,e+VERTS,Verts,NVerts);
  407.         resource_update(iwad,e+LINES,Lines,NLines);
  408.         resource_update(iwad,e+NODES,Nodes,NNodes);
  409.         resource_update(iwad,e+SEGS,Segs,NSegs);
  410.         resource_update(iwad,e+BLKMAPS,blockmaps,NBlockmaps);
  411.         printf("done\n");
  412.       }
  413.       if (strstr(func,"shift") != NULL) {
  414.         DOOM_BLOCKMAP *blockmaps;        /* new BLOCKMAP for shift */
  415.         printf("Shifting level E%dM%d...",iwad->ep,iwad->mp);
  416.         shift(dx,dy,dz,Things,NThings,Verts,NVerts,Nodes,NNodes,Sects,NSects);
  417.         NBlockmaps = blockmap_make(&blockmaps,Lines,NLines,Verts);
  418.         resource_update(iwad,e+THINGS,Things,NThings);
  419.         resource_update(iwad,e+VERTS,Verts,NVerts);
  420.         resource_update(iwad,e+NODES,Nodes,NNodes);
  421.         resource_update(iwad,e+SECTS,Sects,NSects);
  422.         resource_update(iwad,e+BLKMAPS,blockmaps,NBlockmaps);
  423.         printf("done\n");
  424.       }
  425.       if (strstr(func,"emstat") != NULL) {
  426.         printf("Statistics for level E%dM%d...\n",iwad->ep,iwad->mp);
  427.         emstat(iwad,e);
  428.         if (strcmp(func,"emstat") == 0) continue;
  429.       }
  430.       if (strstr(func,"dir") != NULL) {
  431.         printf("Directory for %s...\n",ifile);
  432.         directory(iwad);
  433.         e = iwad->head.count;            /* don't read any more */
  434.         continue;
  435.       }
  436.       if (strlen(extract_list) > 0) {
  437.         printf("Extracting from %s...",ifile);
  438.         good = extract(iwad,extract_list);
  439.         printf(good?"done\n":"failed\n");
  440.         e = iwad->head.count;            /* don't read any more */
  441.       }
  442.       if (strlen(subst_list) > 0) {
  443.         printf("Substituting resources in %s with...",ifile);
  444.         good = substitute(iwad,subst_list);
  445.         printf(good?"done\n":"failed\n");
  446.         e = iwad->head.count;            /* don't read any more */
  447.       }
  448.       if (strlen(merge_list) > 0) {
  449.         printf("Merging %s with...",ifile);
  450.         good = merge(iwad,e,merge_list);
  451.         printf(good?"done\n":"failed\n");
  452.         e = iwad->head.count;            /* don't read any more */
  453.       }
  454.     }else if (iwad->head.count <= e)        /* no more in file */
  455.       printf("no more levels\n");
  456.     else                    /* oops: bogus data */
  457.       printf("failed\n");
  458.     if (good) {                    /* if processed, write it */
  459.       printf("Writing %s file %s...",owad->type==1?"WAD":"patch",ofile);
  460.       good = wad_write(owad,iwad);
  461.       printf(good?"done\n":"failed\n");
  462.     }
  463.   } while (good);
  464.   wad_close(owad);
  465.   wad_close(iwad);
  466.   if (strstr(func,"nodes") != NULL ||        /* display processing time */
  467.       strstr(func,"reject") != NULL) {        /* for compute intensive   */
  468.     totaltime = time(NULL) - begintime;        /* operations              */
  469.     hours = totaltime/3600;
  470.     mins = (totaltime-(hours*3600))/60;
  471.     secs = totaltime-(hours*3600)-(mins*60);
  472.     printf("Processing completed in ");
  473.     if (hours) printf("%ld hours, ",hours);
  474.     if (hours || mins) printf("%ld minutes, ",mins);
  475.     printf("%ld seconds\n",secs);
  476.   }
  477.   return 0;                    /* everything is okay */
  478. }
  479.  
  480.  
  481. /******************************************************************************
  482.     ROUTINE:    dmit(ver,things,nthings,sects,nsects)
  483.     WRITTEN BY:    Robert Fenske, Jr.
  484.     CREATED:    Mar. 1994
  485.     DESCRIPTION:    This routine deathmatch-izes the THINGS and SECTORS
  486.             resources.
  487. ******************************************************************************/
  488. #if defined(ANSI_C)
  489. int dmit(int ver,register DOOM_THING *things,long nthings,DOOM_SECTOR *sects,
  490.          long nsects)
  491. #else
  492. int dmit(ver,things,nthings,sects,nsects)
  493. int ver;
  494. register DOOM_THING *things;
  495. long nthings;
  496. DOOM_SECTOR *sects;
  497. long nsects;
  498. #endif
  499. {
  500.    static int subst[][2] = {            /* item substitutions */
  501.     {0x0002, 0x07E6},            /* player 2 --> invulner sph */
  502.     {0x0003, 0x07E7},            /* player 3 --> berzerk pk */
  503.     {0x0004, 0x07E8},            /* player 4 --> blur sphere */
  504.     {0x0005, 0x07DD},            /* blue key --> soul sphere */
  505.     {0x0006, 0x07E8},            /* yellow key --> blur sph */
  506.     {0x000A, 0x07DD},            /* explod being --> soul sph */
  507.     {0x000C, 0x07DD},            /* explod being --> soul sph */
  508.     {0x000D, 0x07E6},            /* red key --> invulner sph */
  509.     {0x000F, 0x0008},            /* dead marine --> backpack */
  510.     {0x0012, 0x07E3},            /* dead fmr hu --> cmbt arm */
  511.     {0x0013, 0x07E6},            /* dead fmr sa --> invl sph */
  512.     {0x0014, 0x07E2},            /* dead fmr sa --> sec armor */
  513.     {0x0015, 0x07E8},            /* dead demon --> blur sph */
  514.     {0x0016, 0x07E7},            /* dead caco --> berzerk pk */
  515.     {0x0017, 0x07E7},            /* dead skull --> berzerk pk */
  516.     {0x0018, 0x0008},            /* bloody goo --> backpack */
  517.     {0x0022, 0x07DB},            /* candle --> stimpak */
  518.     {0x0023, 0x07F3},            /* candelabra --> barrel */
  519.     {0x0026, 0x0011},            /* red skull ky --> ener pk */
  520.     {0x0027, 0x0011},            /* yel skull ky --> ener pk */
  521.     {0x0028, 0x0011},            /* blu skull ky --> ener pk */
  522.     {0x07D7, 0x0800},            /* clip --> box of ammo */
  523.     {0x07D8, 0x0801},            /* shells --> box of shells */
  524.     {0x07DA, 0x07FE},            /* rocket --> box o rockets */
  525.     {0x07DE, 0x07DB},            /* bonus health --> stimpak */
  526.     {0x07DF, 0x07E2},            /* bonus armor --> armor */
  527.     {0x07E9, 0x07E3},            /* rad suit --> combat arm */
  528.     {0x07FF, 0x0011},            /* energy cell --> ener pk */
  529.     {0x07EA, 0x07DD},            /* map --> soul sphere */
  530.     {0x07EC, 0x07DB}            /* light post --> stimpak */
  531.    };
  532.   int caco = 0, troop = 0, sarg = 0, skull = 0;
  533.   int chainsaw = 0, chaingun = 0, plasma = 0, launcher = 0, bfg = 0;
  534.   register int i, s;
  535.  
  536.   for (i = 0; i < nthings; i++) {
  537.     things[i].flag = 0x17;            /* all skills, net play only */
  538.     if (ver == 1) {
  539.       for (s = 0; s < numelm(subst); s++)    /* do the substitutions */
  540.         if (things[i].item == subst[s][0])
  541.           things[i].item = subst[s][1];
  542.     }else {
  543.       if (things[i].item == 0x07DB || things[i].item == 0x07DC)
  544.         things[i].item = 0x07DE;
  545.     }
  546.     if (is_creature(things[i]))
  547.       if      (caco++ < 2)   things[i].item = 0x0BBD;
  548.       else if (troop++ < 15) things[i].item = 0x0BBC;
  549.       else if (sarg++  < 10) things[i].item = 0x0009;
  550.       else if (skull++ < 15) things[i].item = 0x0BBE;
  551.       else                   things[i].item = ver==1? 0x07DC : 0x07DE;
  552.     if (is_weapon(things[i]))
  553.       if      (chainsaw < 1) chainsaw++, things[i].item = 0x07D5;
  554.       else if (chaingun < 1) chaingun++, things[i].item = 0x07D2;
  555.       else if (plasma   < 1) plasma++,   things[i].item = 0x07D4;
  556.       else if (chaingun < 2) chaingun++, things[i].item = 0x07D2;
  557.       else if (plasma   < 2) plasma++,   things[i].item = 0x07D4;
  558.       else if (launcher < 1) launcher++, things[i].item = 0x07D3;
  559.       else if (bfg      < 1) bfg++,      things[i].item = 0x07D6;
  560.       else                               things[i].item = 0x07D1;
  561.   }
  562.   for (i = 0; i < nsects; i++) {
  563.     if (sects[i].property == 0x07) {        /* remove "acid" */
  564.       sects[i].property = 0x00;
  565.       strcpy(sects[i].floor_desc,"FLAT1_1");
  566.     }else if (sects[i].property == 0x05) {    /* remove health hit */
  567.       sects[i].property = 0x00;
  568.       strcpy(sects[i].floor_desc,"FWATER1");
  569.     }
  570.   }
  571.   return TRUE;
  572. }
  573.  
  574.  
  575. /******************************************************************************
  576.     ROUTINE:    rand_xy_sort(thing1,thing2)
  577.     WRITTEN BY:    Robert Fenske, Jr.
  578.     CREATED:    June 1994
  579.     DESCRIPTION:    This routine is the comparison function for qsort().
  580.             It orders the things by their coordinates.
  581. ******************************************************************************/
  582. #if defined(ANSI_C)
  583. int _Optlink rand_xy_sort(register const void *thing1,
  584.                           register const void *thing2)
  585. #else
  586. local int rand_xy_sort(thing1,thing2)
  587. register DOOM_THING *thing1, *thing2;
  588. #endif
  589. {
  590.   DOOM_THING *t1 = (DOOM_THING *)thing1;
  591.   DOOM_THING *t2 = (DOOM_THING *)thing2;
  592.   int xdel = sgn(t1->x - t2->x);
  593.   int ydel = sgn(t1->y - t2->y);
  594.  
  595.   return xdel != 0 ? xdel : ydel;
  596. }
  597.  
  598.  
  599. /******************************************************************************
  600.     ROUTINE:    rand_item_sort(thing1,thing2)
  601.     WRITTEN BY:    Robert Fenske, Jr.
  602.     CREATED:    June 1994
  603.     DESCRIPTION:    This routine is the comparison function for qsort().
  604.             It orders the things by their item types.
  605. ******************************************************************************/
  606. #if defined(ANSI_C)
  607. int _Optlink rand_item_sort(register const void *thing1,
  608.                             register const void *thing2)
  609. #else
  610. local int rand_item_sort(thing1,thing2)
  611. register DOOM_THING *thing1, *thing2;
  612. #endif
  613. {
  614.   DOOM_THING *t1 = (DOOM_THING *)thing1;
  615.   DOOM_THING *t2 = (DOOM_THING *)thing2;
  616.  
  617.   return t1->item - t2->item;
  618. }
  619.  
  620.  
  621. /******************************************************************************
  622.     ROUTINE:    rand_thing(seed,things,nthings)
  623.     WRITTEN BY:    Robert Fenske, Jr.
  624.     CREATED:    Mar. 1994
  625.     DESCRIPTION:    This routine randomizes the order of the things in
  626.             the THINGS resource.  It sorts the things by item type
  627.             and coordinates first so that the same seed will
  628.             always produce the same new order regardless of the
  629.             original input order.  Deathmatch starts and all
  630.             "furniture" (barrels, lamps, etc) are never moved.
  631. ******************************************************************************/
  632. #if defined(ANSI_C)
  633. int rand_thing(long seed, register DOOM_THING *things, long nthings)
  634. #else
  635. int rand_thing(seed,things,nthings)
  636. long seed;
  637. register DOOM_THING *things;
  638. long nthings;
  639. #endif
  640. {
  641.   int t1, t2;
  642.   DOOM_THING temp;
  643.   register DOOM_THING *items;
  644.   register int i, t;
  645.  
  646.   qsort((char *)things,(int)nthings,sizeof *things,rand_xy_sort);
  647.   items = blockmem(DOOM_THING,nthings);
  648.   for (t = 0; t < nthings; t++) items[t] = things[t];
  649.   qsort((char *)items,(int)nthings,sizeof *items,rand_item_sort);
  650.   srand48((unsigned int)seed);
  651.   for (t = 0; t < nthings; t++) {        /* scramble order here */
  652.     t1 = nthings*drand48(), t2 = nthings*drand48();
  653.     temp = things[t1], things[t1] = things[t2], things[t2] = temp;
  654.     t1 = nthings*drand48(), t2 = nthings*drand48();
  655.     temp = items[t1], items[t1] = items[t2], items[t2] = temp;
  656.   }
  657.   for (i = 0, t = 0; t < nthings; t++)
  658.     if (!is_deathmatch(things[t]) && !is_furniture(things[t])) {
  659.       while (is_deathmatch(items[i]) || is_furniture(items[i]))
  660.         i++;
  661.       things[t].item = items[i  ].item;        /* swapping gets new */
  662.       things[t].flag = items[i++].flag;        /* (x,y) for thing   */
  663.     }
  664.   blockfree(items);
  665.   return TRUE;
  666. }
  667.  
  668.  
  669. /******************************************************************************
  670.     ROUTINE:    flip(things,nthings,verts,nverts,lines,nlines,
  671.                  nodes,nnodes,segs,nsegs)
  672.     WRITTEN BY:    Robert Fenske, Jr.
  673.     CREATED:    Apr. 1994
  674.     DESCRIPTION:    This routine flips the level about the Y axis, so
  675.             effectively reverses the level as far as the player is
  676.             concerned.  Note that flipping about the X axis does
  677.             the same thing from the player's persective.
  678. ******************************************************************************/
  679. #if defined(ANSI_C)
  680. int flip(DOOM_THING *things, long nthings, DOOM_VERT *verts, long nverts,
  681.          DOOM_LINE *lines, long nlines, DOOM_NODE *nodes, long nnodes,
  682.          DOOM_SEGS *segs, long nsegs)
  683. #else
  684. int flip(things,nthings,verts,nverts,lines,nlines,nodes,nnodes,segs,nsegs)
  685. DOOM_THING *things;
  686. long nthings;
  687. DOOM_VERT *verts;
  688. long nverts;
  689. DOOM_LINE *lines;
  690. long nlines;
  691. DOOM_NODE *nodes;
  692. long nnodes;
  693. DOOM_SEGS *segs;
  694. long nsegs;
  695. #endif
  696. {
  697.   unsigned short tang;
  698.   register short temp;
  699.   register int i;
  700.  
  701.   for (i = 0; i < nthings; i++)            /* negate X coordinate */
  702.     things[i].x = -things[i].x;
  703.   for (i = 0; i < nverts; i++)            /* negate X coordinate */
  704.     verts[i].x = -verts[i].x;
  705.   for (i = 0; i < nlines; i++) {        /* swap from and to vertices */
  706.     temp = lines[i].fndx;
  707.     lines[i].fndx = lines[i].tndx;
  708.     lines[i].tndx = temp;
  709.   }
  710.   for (i = 0; i < nnodes; i++) {
  711.     nodes[i].x = -nodes[i].x;            /* negate X coordinate */
  712.     nodes[i].xdel = -nodes[i].xdel;        /* negate X offset */
  713.     temp = -nodes[i].rxmax;            /* swap right and left  */
  714.     nodes[i].rxmax = -nodes[i].lxmin;        /* bounding box X       */
  715.     nodes[i].lxmin = temp;            /* coordinates: min for */
  716.     temp = -nodes[i].rxmin;            /* max and negate       */
  717.     nodes[i].rxmin = -nodes[i].lxmax;
  718.     nodes[i].lxmax = temp;
  719.     temp = nodes[i].rymax;            /* swap right and left */
  720.     nodes[i].rymax = nodes[i].lymax;        /* bounding box Y      */
  721.     nodes[i].lymax = temp;            /* min/max coordinates */
  722.     temp = nodes[i].rymin;
  723.     nodes[i].rymin = nodes[i].lymin;
  724.     nodes[i].lymin = temp;
  725.     temp = nodes[i].nndx[0];            /* swap node subtrees */
  726.     nodes[i].nndx[0] = nodes[i].nndx[1];
  727.     nodes[i].nndx[1] = temp;
  728.   }
  729.   for (i = 0; i < nsegs; i++) {
  730.     temp = segs[i].tndx;            /* swap from and to vertices */
  731.     segs[i].tndx = segs[i].fndx;
  732.     segs[i].fndx = temp;
  733.     tang = -segs[i].angle;            /* negate angle */
  734.     segs[i].angle = tang;
  735.   }
  736.   return TRUE;
  737. }
  738.  
  739.  
  740. /******************************************************************************
  741.     ROUTINE:    shift(dx,dy,dz,things,nthings,verts,nverts,
  742.                   nodes,nnodes,sects,nsects)
  743.     WRITTEN BY:    Robert Fenske, Jr.
  744.     CREATED:    Apr. 1994
  745.     DESCRIPTION:    This routine shifts the level by dx, dy, dz.
  746. ******************************************************************************/
  747. #if defined(ANSI_C)
  748. int shift(int dx,int dy,int dz,DOOM_THING *things, long nthings,
  749.           DOOM_VERT *verts, long nverts, DOOM_NODE *nodes, long nnodes,
  750.           DOOM_SECTOR *sects, long nsects)
  751. #else
  752. int shift(dx,dy,dz,things,nthings,verts,nverts,nodes,nnodes,sects,nsects)
  753. int dx, dy, dz;
  754. DOOM_THING *things;
  755. long nthings;
  756. DOOM_VERT *verts;
  757. long nverts;
  758. DOOM_NODE *nodes;
  759. long nnodes;
  760. DOOM_SECTOR *sects;
  761. long nsects;
  762. #endif
  763. {
  764.   register int i;
  765.  
  766.   for (i = 0; i < nthings; i++)            /* shift things coordinates */
  767.     things[i].x += dx, things[i].y += dy;
  768.   for (i = 0; i < nverts; i++)            /* shift vertices coords */
  769.     verts[i].x += dx, verts[i].y += dy;
  770.   for (i = 0; i < nnodes; i++) {        /* shift nodes coordinates */
  771.     nodes[i].x += dx, nodes[i].y += dy,
  772.     nodes[i].rxmin += dx, nodes[i].rymin += dy,
  773.     nodes[i].rxmax += dx, nodes[i].rymax += dy,
  774.     nodes[i].lxmin += dx, nodes[i].lymin += dy,
  775.     nodes[i].lxmax += dx, nodes[i].lymax += dy;
  776.   }
  777.   for (i = 0; i < nsects; i++) {        /* shift sector heights */
  778.     sects[i].floor_ht += dz;
  779.     sects[i].ceil_ht += dz;
  780.   }
  781.   return TRUE;
  782. }
  783.  
  784.  
  785. /******************************************************************************
  786.     ROUTINE:    emstat(iwad,e)
  787.     WRITTEN BY:    Robert Fenske, Jr.
  788.     CREATED:    June 1994
  789.     DESCRIPTION:    This routine displays statistics about the input level.
  790. ******************************************************************************/
  791. #if defined(ANSI_C)
  792. int emstat(register WAD_INFO *iwad, register int e)
  793. #else
  794. int emstat(iwad,e)
  795. register WAD_INFO *iwad;
  796. register int e;
  797. #endif
  798. {
  799.   DIR_ENTRY *dir = &iwad->dir[e];
  800.   int mlv = -1;                    /* maximum line vertex */
  801.   short xmin, xmax, ymin, ymax;
  802.   short x, y;
  803.   int one_sided = 0;                /* one-sided line count */
  804.   int secret = 0;                /* secret sector count */
  805.   int special = 0;                /* special sector count */
  806.   register int i;
  807.  
  808.   for (i = 0; i < ALL; i++)            /* get any missing resources */
  809.     if (iwad->data[e+i] == NULL) (void)wad_read(iwad,e,1L<<i);
  810.   xmin = ymin = (short)0x7FFF, xmax = ymax = (short)0x8000;
  811.   for (i = 0; i < NLines; i++) {        /* find map min,max x,y */
  812.     x = Verts[Lines[i].fndx].x, y = Verts[Lines[i].fndx].y;
  813.     if (x < xmin) xmin = x;
  814.     if (y < ymin) ymin = y;
  815.     if (xmax < x) xmax = x;
  816.     if (ymax < y) ymax = y;
  817.     x = Verts[Lines[i].tndx].x, y = Verts[Lines[i].tndx].y;
  818.     if (x < xmin) xmin = x;
  819.     if (y < ymin) ymin = y;
  820.     if (xmax < x) xmax = x;
  821.     if (ymax < y) ymax = y;
  822.     if (mlv < Lines[i].fndx) mlv = Lines[i].fndx;/* find highest line vertex */
  823.     if (mlv < Lines[i].tndx) mlv = Lines[i].tndx;
  824.     one_sided += Lines[i].lsidndx == -1;
  825.   }
  826.   for (i = 0; i < NSects; i++) {        /* look for special sectors */
  827.     if (Sects[i].property == 9) secret++;
  828.     if (Sects[i].property != 0) special++;
  829.   }
  830.   printf("\tMap...........(%d,%d) to (%d,%d)\n",xmin,ymin,xmax,ymax);
  831.   printf("\tThings........%ld (%ld bytes)\n",NThings,dir[THINGS].nbytes);
  832.   printf("\tLines.........%ld: %d 1-sided, %ld 2-sided (%ld bytes)\n",
  833.          NLines,one_sided,NLines-one_sided,dir[LINES].nbytes);
  834.   printf("\tSides.........%ld (%ld bytes)\n",NSides,dir[SIDES].nbytes);
  835.   printf("\tVertices......%ld: %d for lines, %ld for segs (%ld bytes)\n",
  836.          NVerts,mlv+1,NVerts-(mlv+1),dir[VERTS].nbytes);
  837.   printf("\tSegs..........%ld (%ld bytes)\n",NSegs,dir[SEGS].nbytes);
  838.   printf("\tSubsectors....%ld (%ld bytes)\n",NSsecs,dir[SSECTS].nbytes);
  839.   printf("\tNodes.........%ld (%ld bytes)\n",NNodes,dir[NODES].nbytes);
  840.   printf("\tSectors.......%ld: %d special, %d secret (%ld bytes)\n",
  841.          NSects,special,secret,dir[SECTS].nbytes);
  842.   for (i = 0; i < NRejects; i++)
  843.     if (Rejects[i] != 0) break;
  844.   printf("\tReject........%s (%ld bytes)\n",
  845.          i < NRejects ? "non-zeroed":"zeroed",dir[REJECTS].nbytes);
  846.   printf("\tBlockmap......%d x %d (%ld bytes)\n",
  847.          Blockmaps!=NULL?Blockmaps[2]:0,Blockmaps!=NULL?Blockmaps[3]:0,
  848.          dir[BLKMAPS].nbytes);
  849.   return TRUE;
  850. }
  851.  
  852.  
  853. /******************************************************************************
  854.     ROUTINE:    directory(iwad)
  855.     WRITTEN BY:    Robert Fenske, Jr.
  856.     CREATED:    Sep. 1994
  857.     DESCRIPTION:    This routine displays the resource directory for the
  858.             input file.
  859. ******************************************************************************/
  860. #if defined(ANSI_C)
  861. int directory(register WAD_INFO *iwad)
  862. #else
  863. int directory(iwad)
  864. register WAD_INFO *iwad;
  865. #endif
  866. {
  867.   char name[20];
  868.   char *periods = "....................";
  869.   int n = 0;                    /* count of level parts */
  870.   long c;                    /* # items / entry */
  871.   register int e;
  872.  
  873.   for (e = 0; e < iwad->head.count; e++) {    /* scan resource directory */
  874.     sprintf(name,"%s%-.*s",
  875.             n ? "  " : "",            /* indent if part of level */
  876.             (int)sizeof(iwad->dir[e].name),iwad->dir[e].name);
  877.     if (1 == sscanf(iwad->dir[e].name,"E%*dM%d",&n) ||
  878.         1 == sscanf(iwad->dir[e].name,"MAP%d",&n)) n = ALL;
  879.     if (n > 0) n--;
  880.     printf("\t%s%.*s%ld bytes",
  881.            name,(int)(strlen(periods)-strlen(name)),periods,iwad->dir[e].nbytes);
  882.     if ((c = resource_count(&iwad->dir[e])) < iwad->dir[e].nbytes)
  883.       printf(" (%ld)",c);
  884.     printf("\n");
  885.   }
  886.   return TRUE;
  887. }
  888.  
  889.  
  890. /******************************************************************************
  891.     ROUTINE:    extract(iwad,list)
  892.     WRITTEN BY:    Robert Fenske, Jr.
  893.     CREATED:    June 1994
  894.     DESCRIPTION:    This routine extracts the named resources in the input
  895.             list from the input file.  It manipulates the resource
  896.             directory so that only those extracted resources are
  897.             present in the directory.  If any of the extracted
  898.             resources are E#M#, all resources associated with that
  899.             level are extracted.  If the input list has a leading
  900.             exclamation point (!), then all resources not in the
  901.             list are extracted.
  902. ******************************************************************************/
  903. #if defined(ANSI_C)
  904. int extract(register WAD_INFO *iwad, char *list)
  905. #else
  906. int extract(iwad,list)
  907. register WAD_INFO *iwad;
  908. char *list;
  909. #endif
  910. {
  911.   char name[sizeof(iwad->dir[0].name)+1+1];
  912.   char *elist = blockmem(char,strlen(list)+1+1);
  913.   int i, n;
  914.   boolean find = TRUE;                /* positive extract flag */
  915.   register int r = 0;                /* # extracted resources */
  916.   register int e = 0;
  917.  
  918.   if (list[0] == '!') {                /* means to extract all    */
  919.     find = !find;                /* resources not specified */
  920.     list = &list[1];
  921.   }
  922.   strcat(strcat(strcpy(elist,","),list),",");    /* bracket list w/commas */
  923.   while (e < iwad->head.count) {        /* scan resource directory */
  924.     sprintf(name,",%-.*s",(int)(sizeof(iwad->dir[e].name)),iwad->dir[e].name);
  925.     if ((strstr(elist,name) != NULL &&        /* found one in list */
  926.          0 <= sscanf(strstr(elist,name),",%*[^,]%n",&n) &&
  927.          n == strlen(name)) == find) {
  928.       printf("%s...",&name[1]);
  929.       if (1 == sscanf(name,",E%*dM%d",&i) ||
  930.           1 == sscanf(name,",MAP%d",&i)) n = ALL;/* get all w/ E#M# or MAP## */
  931.       else                               n = 1;    /* get this resource */
  932.       for (i = 0; i < n; i++, e++) {
  933.         blockcopy(&iwad->dir[r],&iwad->dir[e],sizeof(iwad->dir[e]));
  934.         if (r == 0)
  935.           iwad->dir[r].offset = sizeof iwad->head;
  936.         else
  937.           iwad->dir[r].offset = iwad->dir[r-1].offset + iwad->dir[r-1].nbytes;
  938.         if (iwad->data[e] == NULL) wad_read(iwad,e,1L<<0);
  939.         iwad->data[r] = iwad->data[e];
  940.         iwad->count[r] = iwad->count[e];
  941.         iwad->changed[r++] = TRUE;
  942.       }
  943.     }else
  944.       if (1 == sscanf(name,",E%*dM%d",&i) ||
  945.           1 == sscanf(name,",MAP%d",&i)) e += ALL;
  946.       else                               e += 1;
  947.   }
  948.   iwad->head.count = r;                /* new count of resources */
  949.   iwad->head.ident[0] = 'P';            /* make sure it's a PWAD */
  950.   blockfree(elist);
  951.   return r > 0;
  952. }
  953.  
  954.  
  955. /******************************************************************************
  956.     ROUTINE:    substitute(iwad,list)
  957.     WRITTEN BY:    Robert Fenske, Jr.
  958.     CREATED:    June 1994
  959.     DESCRIPTION:    This routine substitutes the resources in the input
  960.             file with any that are found in the input list.  Any
  961.             unique resources in the input list are ignored.
  962. ******************************************************************************/
  963. #if defined(ANSI_C)
  964. int substitute(register WAD_INFO *iwad, char *list)
  965. #else
  966. int substitute(iwad,list)
  967. register WAD_INFO *iwad;
  968. char *list;
  969. #endif
  970. {
  971.   char sfile[256];                /* file to substitute from */
  972.   int n;
  973.   register WAD_INFO *winfo;
  974.   register int e, i, r;
  975.  
  976.   while (list != NULL && 1 == sscanf(list,"%[^,],",sfile)) {
  977.     winfo = wad_open(sfile,TRUE,FALSE);
  978.     if (winfo == NULL) {
  979.       fprintf(stderr,"unable to open %s for reading\n",sfile);
  980.       return FALSE;
  981.     }
  982.     printf("%s...",sfile);            /* substituting from this */
  983.     for (r = 0; r < winfo->head.count; r++) {
  984.       for (e = 0; e < iwad->head.count; e++)
  985.         if (strncmp(winfo->dir[r].name,iwad->dir[e].name,
  986.                     sizeof(winfo->dir[r].name)) == 0)
  987.           break;                /* found a matching resource */
  988.       if (e < iwad->head.count) {        /* substitute resource */
  989.         if (1 == sscanf(winfo->dir[r].name,"E%*dM%d",&n) ||
  990.             1 == sscanf(winfo->dir[r].name,"MAP%d",&n))
  991.           n = ALL;                /* get all w/ E#M# or MAP## */
  992.         else
  993.           n = 1;                /* get this resource */
  994.         (void)wad_read(winfo,r,~(~0L<<n));
  995.         for (i = 0; i < n; i++)
  996.           resource_update(iwad,e+i,winfo->data[r+i],winfo->count[r+i]);
  997.         r += n-1;
  998.       }
  999.       if (1 == sscanf(winfo->dir[r].name,"E%*dM%d",&n) ||
  1000.           1 == sscanf(winfo->dir[r].name,"MAP%d",&n))
  1001.         r += ALL-1;
  1002.     }
  1003.     wad_close(winfo);
  1004.     list = strstr(list,",");
  1005.     if (list != NULL) list++;
  1006.   }
  1007.   return TRUE;
  1008. }
  1009.  
  1010.  
  1011. /******************************************************************************
  1012.     ROUTINE:    merge(iwad,e,list)
  1013.     WRITTEN BY:    Robert Fenske, Jr.
  1014.     CREATED:    June 1994
  1015.     DESCRIPTION:    This routine merges the files in the input list with
  1016.             the currently open input file referenced by iwad.
  1017.             Any level data matching level data in the input file
  1018.             (i.e., levels that have the same E#M#) is combined
  1019.             into a single, larger level.  Note that this combined
  1020.             level will require the nodes, blockmap, and rejects to
  1021.             be rebuilt.  Any unique resources in the input merge
  1022.             list are added to the input file as well.  Non-unique
  1023.             resources other than level data are not added, e.g. if
  1024.             DEMO1 is in one of the merge list files and the input
  1025.             file already as a DEMO1, it is not added to the input
  1026.             file.
  1027. ******************************************************************************/
  1028. #if defined(ANSI_C)
  1029. int merge(register WAD_INFO *iwad, int e, char *list)
  1030. #else
  1031. int merge(iwad,e,list)
  1032. register WAD_INFO *iwad;
  1033. int e;
  1034. char *list;
  1035. #endif
  1036. {
  1037. #define merge_resource(b,t,i,r,d1,c1,d2,c2)    \
  1038.             ((b) = (char *)blockmem(t,((c1)+(c2))*sizeof(t)),\
  1039.              blockcopy((b),(d1),(c1)*sizeof(t)),\
  1040.              blockcopy(&(b)[(c1)*sizeof(t)],(d2),(c2)*sizeof(t)),\
  1041.              resource_update((i),(r),(b),(c1)+(c2)),\
  1042.              (c1) += (c2))
  1043. #define add_resource(b,t,d,c,r)    \
  1044.             ((b) = (char *)blockmem(t,(c)),\
  1045.              blockcopy((b),(d),((c)-1)*sizeof(t)),\
  1046.              ((t *)(b))[(c)-1] = (r),\
  1047.              blockfree(d),\
  1048.              (d) = (t *)(b))
  1049.   char mfile[256];                /* file to merge */
  1050.   int maxtag;                    /* maximum line/sector tag */
  1051.   int maxvert;                    /* maximum input file vertex */
  1052.   int maxwvert;                    /* max merge list file vert */
  1053.   DOOM_LINE *wlines;
  1054.   int i, n;
  1055.   register char *buf;
  1056.   register WAD_INFO *winfo;
  1057.   register int r;
  1058.  
  1059.   for (i = 0; i < ALL; i++)            /* get any missing resources */
  1060.     if (iwad->data[e+i] == NULL) (void)wad_read(iwad,e,1L<<i);
  1061.   while (list != NULL && 1 == sscanf(list,"%[^,],",mfile)) {
  1062.     winfo = wad_open(mfile,TRUE,FALSE);
  1063.     if (winfo == NULL) {
  1064.       fprintf(stderr,"unable to open %s for reading\n",mfile);
  1065.       return FALSE;
  1066.     }
  1067.     printf("%s...",mfile);            /* merging this file */
  1068.     for (r = 0; r < winfo->head.count; r++)
  1069.       if (strncmp(winfo->dir[r].name,iwad->dir[e+MAINS].name,
  1070.                   sizeof(winfo->dir[r].name)) == 0)
  1071.         break;
  1072.     if (r < winfo->head.count) {        /* found matching level */
  1073.       (void)wad_read(winfo,r,~(~0L<<ALL));    /* get all level data */
  1074.       merge_resource(buf,DOOM_THING,iwad,e+THINGS,Things,NThings,
  1075.                      winfo->data[r+THINGS],winfo->count[r+THINGS]);
  1076.       maxtag = maxvert = 0;
  1077.       for (i = 0; i < NLines; i++) {
  1078.         if (maxvert < Lines[i].fndx) maxvert = Lines[i].fndx;
  1079.         if (maxvert < Lines[i].tndx) maxvert = Lines[i].tndx;
  1080.         if (maxtag < Lines[i].sect_tag) maxtag = Lines[i].sect_tag;
  1081.       }
  1082.       maxwvert = 0;
  1083.       wlines = (DOOM_LINE *)winfo->data[r+LINES];
  1084.       for (i = 0; i < winfo->count[r+LINES]; i++) {
  1085.         if (maxwvert < wlines[i].fndx) maxwvert = wlines[i].fndx;
  1086.         if (maxwvert < wlines[i].tndx) maxwvert = wlines[i].tndx;
  1087.         wlines[i].fndx += maxvert+1;
  1088.         wlines[i].tndx += maxvert+1;
  1089.         if (wlines[i].sect_tag != 0)
  1090.           wlines[i].sect_tag += maxtag;
  1091.         wlines[i].rsidndx += NSides;
  1092.         if (wlines[i].lsidndx != -1) wlines[i].lsidndx += NSides;
  1093.       }
  1094.       merge_resource(buf,DOOM_LINE,iwad,e+LINES,Lines,NLines,
  1095.                      winfo->data[r+LINES],winfo->count[r+LINES]);
  1096.       for (i = 0; i < winfo->count[r+SIDES]; i++)
  1097.         ((DOOM_SIDE *)winfo->data[r+SIDES])[i].sectndx += NSects;
  1098.       merge_resource(buf,DOOM_SIDE,iwad,e+SIDES,Sides,NSides,
  1099.                      winfo->data[r+SIDES],winfo->count[r+SIDES]);
  1100.       NVerts = maxvert+1;
  1101.       merge_resource(buf,DOOM_VERT,iwad,e+VERTS,Verts,NVerts,
  1102.                      winfo->data[r+VERTS],maxwvert+1);
  1103.       resource_update(iwad,e+SEGS,Segs,0L);    /* kill these because they */
  1104.       resource_update(iwad,e+SSECTS,Ssecs,0L);    /* will have to be rebuilt */
  1105.       resource_update(iwad,e+NODES,Nodes,0L);
  1106.       for (i = 0; i < winfo->count[r+SECTS]; i++)
  1107.         if (((DOOM_SECTOR *)winfo->data[r+SECTS])[i].line_tag != 0)
  1108.           ((DOOM_SECTOR *)winfo->data[r+SECTS])[i].line_tag += maxtag;
  1109.       merge_resource(buf,DOOM_SECTOR,iwad,e+SECTS,Sects,NSects,
  1110.                      winfo->data[r+SECTS],winfo->count[r+SECTS]);
  1111.       resource_update(iwad,e+REJECTS,Rejects,0L);/* these two will have to */
  1112.       resource_update(iwad,e+BLKMAPS,Blockmaps,0L);/* be rebuilt also      */
  1113.     }
  1114.     for (r = 0; r < winfo->head.count; r++) {    /* search for unique rsrcs */
  1115.       for (i = 0; i < iwad->head.count; i++)
  1116.         if (strncmp(winfo->dir[r].name,iwad->dir[i].name,
  1117.                     sizeof(winfo->dir[r].name)) == 0)
  1118.           break;
  1119.       if (i == iwad->head.count) {        /* add in new resource */
  1120.        if (1 == sscanf(winfo->dir[r].name,"E%*dM%d",&n) ||
  1121.            1 == sscanf(winfo->dir[r].name,"MAP%d",&n))
  1122.          n = ALL;                /* get all w/ E#M# or MAP## */
  1123.        else
  1124.          n = 1;                    /* get this resource */
  1125.        (void)wad_read(winfo,r,~(~0L<<n));
  1126.        for (i = 0; i < n; i++) {
  1127.        add_resource(buf,DIR_ENTRY,iwad->dir,iwad->head.count+1,winfo->dir[r+i]);
  1128.        add_resource(buf,char *,iwad->data,iwad->head.count+1,winfo->data[r+i]);
  1129.        add_resource(buf,long,iwad->count,iwad->head.count+1,winfo->count[r+i]);
  1130.        add_resource(buf,boolean,iwad->changed,iwad->head.count+1,TRUE);
  1131.        iwad->head.count++;
  1132.        }
  1133.        r += n-1;
  1134.       }
  1135.     }
  1136.     wad_close(winfo);
  1137.     list = strstr(list,",");
  1138.     if (list != NULL) list++;
  1139.   }
  1140.   return TRUE;
  1141. }
  1142.